import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * Stream流的使用
 */
public class StreamUse {

    public static void main(String[] args) {
//        use();
//        filter();
//        polymerization();
//        mapping();
//        reduce();
        collect();
    }

    /**
     * 遍历
     */
    private static void use(){
        List<Integer> list = Arrays.asList(7, 6, 9, 3, 8, 2, 1);

        // 遍历输出符合条件的元素
        list.stream().filter(x -> x > 6).forEach(System.out::println);
        // 匹配第一个
        Optional<Integer> findFirst = list.stream().filter(x -> x > 6).findFirst();
        // 匹配任意【适用于并行流】
        Optional<Integer> findAny = list.parallelStream().filter(x -> x > 6).findAny();
        // 是否包含符合特定条件的元素
        boolean anyMatch = list.stream().anyMatch(x -> x > 6);
        System.out.println("匹配第一个值：" + findFirst.get());
        System.out.println("匹配任意一个值：" + findAny.get());
        System.out.println("是否存在大于6的值：" + anyMatch);
    }


    /**
     * 筛选
     */
    private static void filter(){
        List<Integer> list = Arrays.asList(6, 7, 3, 8, 1, 2, 9);
        list.stream().filter(x -> x > 7).forEach(System.out::println);


        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        personList.stream().filter(x -> x.getSalary() > 8000).map(item -> item.getName())
                .collect(Collectors.toList()).forEach(System.out::println);

    }


    /**
     * 聚合
     */
    private static void polymerization(){
        // -----------------获取String集合中最长的元素-----------------
        List<String> list = Arrays.asList("adnm", "admmt", "pot", "xbangd", "weoujgsd");
        Optional<String> maxStr = list.stream().max(Comparator.comparing(String::length));
        System.out.println("最长的字符串：" + maxStr.get());


        // -----------------获取Integer集合中的最大值-----------------
        List<Integer> list1 = Arrays.asList(7, 6, 9, 4, 11, 6);
        // 自然排序
        Optional<Integer> maxOpt = list1.stream().max(Integer::compareTo);
        // 自定义排序
        Optional<Integer> max2 = list1.stream().max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1.compareTo(o2);
            }
        });
        System.out.println("自然排序的最大值：" + maxOpt.get());
        System.out.println("自定义排序的最大值：" + max2.get());


        // -----------------获取员工工资最高的人-----------------
        List<Person> personList = new ArrayList<>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
        Optional<Person> max = personList.stream().max(Comparator.comparingInt(Person::getSalary));
        System.out.println("员工工资最大值：" + max.get().getSalary());


        list1 = Arrays.asList(7, 6, 4, 8, 2, 11, 9);
        long count = list1.stream().filter(item -> item > 6).count();
        System.out.println("list中大于6的元素个数：" + count);

    }


    /**
     * 映射
     * map：接收一个函数作为参数，该函数会被应用到每个元素上，并将其映射成一个新的元素。
     * flatMap：接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流
     */
    private static void mapping(){
        //-----------------英文字符串数组的元素全部改为大写，整数数组每个元素+3-----------------
        String[] strArr = { "abcd", "bcdd", "defde", "fTr" };
        List<String> strList = Arrays.stream(strArr).map(String::toUpperCase).collect(Collectors.toList());

        List<Integer> intList = Arrays.asList(1, 3, 5, 7, 9, 11);
        List<Integer> intListNew = intList.stream().map(x -> x + 3).collect(Collectors.toList());

        System.out.println("每个元素大写：" + strList);
        System.out.println("每个元素+3：" + intListNew);


        //-----------------将员工的薪资全部增加1000-----------------
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));

        // 不改变原来员工集合的方式
        List<Person> personListNew = personList.stream().map(person -> {
            Person personNew = new Person(person.getName(), 0, 0, null, null);
            personNew.setSalary(person.getSalary() + 1000);
            return personNew;
        }).collect(Collectors.toList());
        System.out.println("一次改动前：" + personList.get(0).getName() + "-->" + personList.get(0).getSalary());
        System.out.println("一次改动后：" + personListNew.get(0).getName() + "-->" + personListNew.get(0).getSalary());

        // 改变原来员工集合的方式
        List<Person> personListNew2 = personList.stream().map(person -> {
            person.setSalary(person.getSalary() + 1000);
            return person;
        }).collect(Collectors.toList());
        System.out.println("二次改动前：" + personList.get(0).getName() + "-->" + personList.get(0).getSalary());
        System.out.println("二次改动后：" + personListNew2.get(0).getName() + "-->" + personListNew2.get(0).getSalary());


        //-----------------将两个字符数组合并成一个新的字符数组-----------------
        List<String> list = Arrays.asList("m,k,l,a", "1,3,5,7");
        List<String> listNew = list.stream().flatMap(s -> {
            // 将每个元素转换成一个stream
            String[] split = s.split(",");
            Stream<String> s2 = Arrays.stream(split);
            return s2;
        }).collect(Collectors.toList());

        System.out.println("处理前的集合：" + list);
        System.out.println("处理后的集合：" + listNew);


    }


    /**
     * 规约
     * 是把一个流缩减成一个值，能实积现对集合求和、求乘和求最值操作。
     */
    private static void reduce(){
        //--------------求Integer集合的元素之和、乘积和最大值--------------
        List<Integer> list = Arrays.asList(1, 3, 2, 8, 11, 4);
        // 方式一
        Optional<Integer> reduce = list.stream().reduce((x, y) -> x + y);
        // 方式二
        Optional<Integer> reduce1 = list.stream().reduce(Integer::sum);
        // 方式三
        Integer sum3 = list.stream().reduce(0, Integer::sum);

        //--------------求乘积--------------
        Optional<Integer> product = list.stream().reduce((x, y) -> x * y);

        //--------------求最大值--------------
        // 求最大值方式1
        Optional<Integer> max = list.stream().reduce((x, y) -> x > y ? x : y);
        // 求最大值写法2
        Integer max2 = list.stream().reduce(list.get(0), Integer::max);

        //--------------求所有员工的工资之和和最高工资--------------
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        personList.add(new Person("Owen", 9500, 25, "male", "New York"));
        personList.add(new Person("Alisa", 7900, 26, "female", "New York"));
        // 最高工资
        Optional<Integer> reduce2 = personList.stream().map(item -> item.getSalary()).reduce(Integer::max);
        Optional<Integer> reduce3 = personList.stream().map(item -> item.getSalary()).reduce((x, y) -> x > y ? x : y);
        // 工资之和
        Optional<Integer> reduce4 = personList.stream().map(item -> item.getSalary()).reduce((x, y) -> x + y);



    }


    /**
     * 收集
     * 把一个流收集起来，最终可以是收集成一个值也可以收集成一个新的集合
     */
    private static void collect(){
        //-----------------归集（toList、toSet、toMap）-----------------
        // toList，toSet
        List<Integer> list = Arrays.asList(1, 6, 3, 4, 6, 7, 9, 6, 20);
        List<Integer> listNew = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toList());
        Set<Integer> set = list.stream().filter(x -> x % 2 == 0).collect(Collectors.toSet());

        // toMap
        List<Person> personList = new ArrayList<Person>();
        personList.add(new Person("Tom", 8900, 23, "male", "New York"));
        personList.add(new Person("Jack", 7000, 25, "male", "Washington"));
        personList.add(new Person("Lily", 7800, 21, "female", "Washington"));
        personList.add(new Person("Anni", 8200, 24, "female", "New York"));
        Map<String, Integer> collectMap = personList.stream().collect(Collectors.toMap(item -> item.getName(), item -> item.getAge()));

        //-----------------统计（count、averaging）-----------------
        // 求总数
        Long collect = personList.stream().collect(Collectors.counting());
        // 求平均工资
        Double collect1 = personList.stream().collect(Collectors.averagingDouble(item -> item.getSalary()));


        //------------------- 分组（partitioningBy、groupingBy）-------------------
        // 将员工按薪资是否高于8000分为两部分；将员工按性别和地区分组
        // 将员工按薪资是否高于8000分组
        Map<Boolean, List<Person>> part = personList.stream().collect(Collectors.partitioningBy(x -> x.getSalary() > 8000));
        // 将员工按性别分组
        Map<String, List<Person>> group = personList.stream().collect(Collectors.groupingBy(Person::getSex));
        // 将员工先按性别分组，再按地区分组
        Map<String, Map<String, List<Person>>> group2 = personList.stream().collect(Collectors.groupingBy(Person::getSex, Collectors.groupingBy(Person::getArea)));
        System.out.println("员工按薪资是否大于8000分组情况：" + part);
        System.out.println("员工按性别分组情况：" + group);
        System.out.println("员工按性别、地区：" + group2);


        //-------------------接合（joining）-------------------
        // joining可以将stream中的元素用特定的连接符（没有的话，则直接连接）连接成一个字符串。
        String collect2 = personList.stream().map(Person::getName).collect(Collectors.joining(","));



        //-------------------规约（reducing）-------------------
        // Collectors类提供的reducing方法，相比于stream本身的reduce方法，增加了对自定义归约的支持。
        Integer sum = personList.stream().collect(Collectors.reducing(0, Person::getSalary, (i, j) -> (i + j - 5000)));
        System.out.println("员工扣税薪资总和：" + sum);


        //-------------------排序（sorted）-------------------
        // 按工资升序排序（自然排序）
        List<String> collect3 = personList.stream().sorted(Comparator.comparing(Person::getSalary)).map(Person::getName).collect(Collectors.toList());
        // 按工资倒序排序
        List<String> collect4 = personList.stream().sorted(Comparator.comparing(Person::getSalary).reversed()).map(Person::getName).collect(Collectors.toList());
        //  先按工资再按年龄自定义排序（降序）
        List<Person> collect5 = personList.stream().sorted((x, y) -> {
            if (x.getSalary() == y.getSalary()) return x.getAge() - y.getAge();
            else return x.getSalary() - y.getSalary();
        }).collect(Collectors.toList());


        // 去重、合并（distinct、skip、limit）
        String[] arr1 = { "a", "b", "c", "d" };
        String[] arr2 = { "d", "e", "f", "g" };

        // 转为stream流后操作
        Stream<String> stream1 = Stream.of(arr1);
        Stream<String> stream2 = Stream.of(arr2);
        // concat:合并两个流 distinct：去重
        List<String> newList = Stream.concat(stream1, stream2).distinct().collect(Collectors.toList());
        // limit：限制从流中获得前n个数据
        List<Integer> collect11 = Stream.iterate(1, x -> x + 2).limit(10).collect(Collectors.toList());
        // skip：跳过前n个数据  这里的1代表把1代入后边的计算表达式
        List<Integer> collect22 = Stream.iterate(1, x -> x + 2).skip(1).limit(5).collect(Collectors.toList());

        System.out.println("流合并：" + newList);
        System.out.println("limit：" + collect11);
        System.out.println("skip：" + collect22);



    }


    static class Person{
        private String name;  // 姓名
        private int salary; // 薪资
        private int age; // 年龄
        private String sex; //性别
        private String area;  // 地区

        // 构造方法
        public Person(String name, int salary, int age,String sex,String area) {
            this.name = name;
            this.salary = salary;
            this.age = age;
            this.sex = sex;
            this.area = area;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public int getSalary() {
            return salary;
        }

        public void setSalary(int salary) {
            this.salary = salary;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }

        public String getSex() {
            return sex;
        }

        public void setSex(String sex) {
            this.sex = sex;
        }

        public String getArea() {
            return area;
        }

        public void setArea(String area) {
            this.area = area;
        }
    }
}
